package com.gzx.plugin.tjzy.modular.sms.batch.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.aliyun.oss.ServiceException;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fhs.trans.service.impl.TransService;
import com.google.common.collect.Lists;
import com.gzx.plugin.tjzy.modular.blacklist.service.GzxBlackListService;
import com.gzx.plugin.tjzy.modular.caffeine.UserCaffeine;
import com.gzx.plugin.tjzy.modular.complaint.entity.bo.GzxComplaintUserQueryBo;
import com.gzx.plugin.tjzy.modular.complaint.entity.vo.GzxComplaintUserVo;
import com.gzx.plugin.tjzy.modular.complaint.service.GzxComplaintUserService;
import com.gzx.plugin.tjzy.modular.founds.bo.GzxTransMethodBo;
import com.gzx.plugin.tjzy.modular.founds.constant.FoundsConstant;
import com.gzx.plugin.tjzy.modular.founds.service.GzxFoundsFlowService;
import com.gzx.plugin.tjzy.modular.founds.vo.GzxFoundsAvailableVo;
import com.gzx.plugin.tjzy.modular.phone.filter.entity.GzxPhoneFilter;
import com.gzx.plugin.tjzy.modular.phone.filter.service.GzxPhoneFilterService;
import com.gzx.plugin.tjzy.modular.project.service.GzxProjectService;
import com.gzx.plugin.tjzy.modular.sms.api.strategy.SMSStrategy;
import com.gzx.plugin.tjzy.modular.sms.api.strategy.SMSStrategyFactory;
import com.gzx.plugin.tjzy.modular.sms.batch.bo.GzxSmsBatchAddBo;
import com.gzx.plugin.tjzy.modular.sms.batch.bo.GzxSmsBatchQueryBo;
import com.gzx.plugin.tjzy.modular.sms.batch.consts.SMSConstant;
import com.gzx.plugin.tjzy.modular.sms.batch.entity.GzxSmsBatch;
import com.gzx.plugin.tjzy.modular.sms.batch.entity.GzxSmsDetail;
import com.gzx.plugin.tjzy.modular.sms.batch.entity.GzxSmsDetailCount;
import com.gzx.plugin.tjzy.modular.sms.batch.enums.GzxSmsBatchSendStatusEnum;
import com.gzx.plugin.tjzy.modular.sms.batch.enums.GzxSmsBatchStatusEnum;
import com.gzx.plugin.tjzy.modular.sms.batch.enums.GzxSmsSendStatusEnum;
import com.gzx.plugin.tjzy.modular.sms.batch.listener.SMSExcelListener;
import com.gzx.plugin.tjzy.modular.sms.batch.mapper.GzxSmsBatchMapper;
import com.gzx.plugin.tjzy.modular.sms.batch.mapper.GzxSmsDetailMapper;
import com.gzx.plugin.tjzy.modular.sms.batch.service.GzxSmsService;
import com.gzx.plugin.tjzy.modular.sms.batch.vo.*;
import com.gzx.plugin.tjzy.modular.sms.template.entity.GzxSmsTemplate;
import com.gzx.plugin.tjzy.modular.sms.template.mapper.GzxSmsTemplateMapper;
import com.gzx.plugin.tjzy.modular.sms.whitelist.bo.GzxSmsWhitelistQueryBo;
import com.gzx.plugin.tjzy.modular.sms.whitelist.entity.GzxSmsWhitelist;
import com.gzx.plugin.tjzy.modular.sms.whitelist.service.GzxSmsWhitelistService;
import com.gzx.plugin.tjzy.modular.supplier.entity.GzxSupplier;
import com.gzx.plugin.tjzy.modular.supplier.service.GzxSupplierService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.common.consts.BatchNumberConstant;
import vip.xiaonuo.common.consts.GzxTenantConstant;
import vip.xiaonuo.common.consts.IntConstant;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.util.*;
import vip.xiaonuo.common.wechat.enums.WechatNotificationType;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.lang.StringEscapeUtils.escapeSql;

/**
 * @author zjt
 * @description
 * @date 2025/3/6 17:21
 */
@Slf4j
@DS("sms")
@Service
@RequiredArgsConstructor
public class GzxSmsServiceImpl implements GzxSmsService {

    private final GzxSmsBatchMapper smsBatchMapper;

    private final GzxSmsDetailMapper smsDetailMapper;

    private final GzxSmsTemplateMapper smsTemplateMapper;

    private final SMSStrategyFactory smsStrategyFactory;

    //充值电话短信流水记录
    private final GzxFoundsFlowService foundsFlowService;

    private final GzxSupplierService supplierService;

    //白名单
    private final GzxSmsWhitelistService whitelistService;

    /**
     * 投诉用户服务
     */
    private final GzxComplaintUserService complaintUserService;

    //项目信息
    private final GzxProjectService projectService;

    //手机过滤
    private final GzxPhoneFilterService phoneFilterService;

    //号码库黑名单
    private final GzxBlackListService blackListService;

    private final UserCaffeine userCaffeine;

    private final TransService transService;

    /**
     * 分页查询短信发送主表
     *
     * @param smsBatchQueryBo
     * @return
     */
    @Override
    public Page<GzxSmsBatchVo> pageList(GzxSmsBatchQueryBo smsBatchQueryBo) {
        Set<String> batchNumberSet = new HashSet<>();
        if (StringUtils.isNotBlank(smsBatchQueryBo.getPhone())) {
            batchNumberSet.addAll(smsDetailMapper.selectList(Wrappers.<GzxSmsDetail>lambdaQuery()
                    .eq(GzxSmsDetail::getToPhone, CommonCryptogramUtil.doSm1AesEncrypt(smsBatchQueryBo.getPhone()))).stream().map(GzxSmsDetail::getBatchNumber).collect(Collectors.toSet()));
            batchNumberSet.add("NO");
        }
        Page<GzxSmsBatchVo> page = smsBatchMapper.selectVoPage(new Page<>(smsBatchQueryBo.getCurrent(), smsBatchQueryBo.getSize()),
                Wrappers.<GzxSmsBatch>lambdaQuery()
                        .in(StringUtils.isNotBlank(smsBatchQueryBo.getPhone()), GzxSmsBatch::getBatchNumber, batchNumberSet)
                        .eq(smsBatchQueryBo.getCreateUser() != null, GzxSmsBatch::getCreateUser, smsBatchQueryBo.getCreateUser())
                        .eq(smsBatchQueryBo.getDeptId() != null, GzxSmsBatch::getCreateDept, smsBatchQueryBo.getDeptId())
                        .between(StringUtils.isNotBlank(smsBatchQueryBo.getBeginTime()) && StringUtils.isNotBlank(smsBatchQueryBo.getEndTime()), GzxSmsBatch::getCreateTime, smsBatchQueryBo.getBeginTime(), smsBatchQueryBo.getEndTime())
                        .orderByDesc(GzxSmsBatch::getId));
        if (CollectionUtil.isNotEmpty(page.getRecords())) {
            //设置成功、失败，发送状态
            List<String> batchNumberList = page.getRecords().stream().map(GzxSmsBatchVo::getBatchNumber).toList();
            List<GzxSmsDetailCount> smsDetailCountList = smsDetailMapper.countByBatchNumber(batchNumberList);
            Map<String, GzxSmsDetailCount> detailCountMap = smsDetailCountList.stream().collect(Collectors.toMap(GzxSmsDetailCount::getBatchNumber, Function.identity()));
            for (GzxSmsBatchVo smsBatchVo : page.getRecords()) {
                if (detailCountMap.containsKey(smsBatchVo.getBatchNumber())) {
                    GzxSmsDetailCount smsDetailCount = detailCountMap.get(smsBatchVo.getBatchNumber());
                    smsBatchVo.setSuccessQuantity(smsDetailCount.getSuccessQuantity());
                    smsBatchVo.setFailQuantity(smsDetailCount.getFailQuantity());
                    smsBatchVo.setNoSendQuantity(smsDetailCount.getNoSendQuantity());
                    smsBatchVo.setCompletedQuantity(smsBatchVo.getTotalQuantity() - smsBatchVo.getNoSendQuantity());
                    if (smsDetailCount.getNoSendQuantity() == 0) {
                        smsBatchVo.setSendStatus(GzxSmsBatchSendStatusEnum.COMPLETED.getCode());
                    } else if (smsBatchVo.getTotalQuantity().equals(smsDetailCount.getNoSendQuantity())) {
                        smsBatchVo.setSendStatus(GzxSmsBatchSendStatusEnum.PENDING_SEND.getCode());
                    } else {
                        smsBatchVo.setSendStatus(GzxSmsBatchSendStatusEnum.SENDING.getCode());
                    }
                }
            }
        }
        return page;
    }

    /**
     * 查询短信发送详情
     *
     * @param smsBatchQueryBo
     * @return
     */
    @Override
    public Page<GzxSmsDetailVo> pageDetail(GzxSmsBatchQueryBo smsBatchQueryBo) {
        Page<GzxSmsDetailVo> page = smsDetailMapper.selectVoPage(new Page<>(smsBatchQueryBo.getCurrent(), smsBatchQueryBo.getSize()), buildDetailQueryWrapper(smsBatchQueryBo));
        if (CollectionUtil.isNotEmpty(page.getRecords())) {
            page.getRecords().forEach(s -> s.setNoSensitivePhone(s.getToPhone()));
        }
        return page;
    }

    /**
     * 导入短信发送数据
     */
    @Override
    public String importExcel(GzxSmsBatchAddBo smsBatchAddBo, MultipartFile file) {
        StringBuilder str = new StringBuilder();
        long start = System.currentTimeMillis();
        log.info("短信导入参数 {} ", smsBatchAddBo);
        //根据模版查询供应商优先级
        String templateCode = smsBatchAddBo.getTemplateCode();
        GzxSmsTemplate smsTemplate = smsTemplateMapper.selectOne(new LambdaQueryWrapper<GzxSmsTemplate>()
                .eq(GzxSmsTemplate::getTemplateCode, templateCode));
        if (smsTemplate == null) {
            throw new ServiceException("模版不存在");
        }
        //生成短信发送批次号
        List<GzxSmsExcelVo> smsExcelVoList = CommonExcelUtil.importExcel(file, GzxSmsExcelVo.class, new SMSExcelListener()).stream()
                .collect(Collectors.toMap(
                        GzxSmsExcelVo::getToPhone,
                        person -> person,
                        (existing, replacement) -> existing
                )).values().stream().toList();
        List<GzxSmsDetail> smsDetailList = MapstructUtils.convert(smsExcelVoList, GzxSmsDetail.class);
        if (CollUtil.isEmpty(smsDetailList)) {
            throw new CommonException("Excel为空");
        }
        //获取所有的投诉用户
        List<GzxComplaintUserVo> complaintUser = complaintUserService.listAll(new GzxComplaintUserQueryBo());
        //从smsDetailList中把complaintUser的手机号相同的数据去掉
        smsDetailList = smsDetailList.stream()
                .filter(smsDetail -> complaintUser.stream()
                        .noneMatch(detail -> detail.getMobile().equals(smsDetail.getToPhone()))).toList();

        //计算这个项目的可用余额是否足够发送短信
        String[] order = smsBatchAddBo.getSupplierOrder().split(",");
        //获取供应商一条短信的价格
        GzxSupplier supplier = supplierService.getSupplierByCode(order[0]);
        if (Objects.isNull(supplier) || Objects.isNull(supplier.getBillingMethod())) {
            throw new CommonException("供应商单价为空");
        }

        //本次发短信花费多少钱
        BigDecimal billingMethod = new BigDecimal(supplier.getBillingMethod());
        BigDecimal totalCost = billingMethod.multiply(new BigDecimal(smsDetailList.size()));

        //获取项目可用余额
        GzxFoundsAvailableVo available = foundsFlowService.getAvaliableByCode(smsBatchAddBo.getProjectCode());
        if (Objects.isNull(available) || available.getFoundsAvailable().compareTo(totalCost) < 0) {
            throw new CommonException("可用余额不足");
        }
        log.info("获取项目可用余额 {} ", available);

        //根据项目编码获取产品编码
        String productCode = projectService.getProductCodeByProjectCode(smsBatchAddBo.getProjectCode());
        log.info("根据项目编码获取到的产品编码是 -> {} ", productCode);

        //过滤白名单
        smsDetailList = getSmsDetails(productCode, smsDetailList, str);

        //选中状态才过滤
        if (smsBatchAddBo.isProjectBlackList()) {
            //过滤项目手机号黑名单（在黑名单里的排除掉）
            List<GzxPhoneFilter> phoneFilters = phoneFilterService.selectList(new LambdaQueryWrapper<GzxPhoneFilter>().select(GzxPhoneFilter::getPhoneNumber).eq(GzxPhoneFilter::getProjectCode, smsBatchAddBo.getProjectCode()));

            log.info("过滤手机号黑名单 过滤前 -> {} ", smsDetailList.size());
            //从smsDetailList把phoneFilters手机号相同的数据去掉
            smsDetailList = smsDetailList.stream()
                    .filter(smsDetail -> phoneFilters.stream()
                            .noneMatch(detail -> detail.getPhoneNumber().equals(smsDetail.getToPhone()))).toList();
            log.info("过滤手机号黑名单 过滤后 -> {} ", smsDetailList.size());
        }

        //过滤停机空号黑名单
//        List<GzxBlackListVo> blackList = blackListService.queryAll(BlackListConstant.STOP_NULL_TYPE);
//        //从smsDetailList中把blackList手机号相同的数据去掉
//        log.info("过滤号码库黑名单 过滤前 {} 条", smsDetailList.size());
//        smsDetailList = smsDetailList.stream()
//                .filter(smsDetail -> blackList.stream()
//                        .noneMatch(detail -> detail.getPhone().equals(smsDetail.getToPhone()))).toList();
        log.info("过滤号码库黑名单 过滤后 {} 条", smsDetailList.size());
        if (CollectionUtil.isNotEmpty(smsDetailList)) {
            StringBuilder sb = new StringBuilder("SMS").append(CommonBatchNumberUtil.getBatchNumber(BatchNumberConstant.SMS_BATCH_NUMBER));
            String templateContent = smsTemplate.getTemplateContent();
            String tenantId = StpLoginUserUtil.getLoginUser().getTenantId();
            String userId = StpLoginUserUtil.getLoginUser().getId();
            String projectCode = smsBatchAddBo.getProjectCode();
            for (GzxSmsDetail smsDetail : smsDetailList) {
                smsDetail.setBatchNumber(sb.toString());
                smsDetail.setProjectCode(projectCode);
                smsDetail.setSignCity(smsBatchAddBo.getSignCity());
                smsDetail.setToPhone(CommonCryptogramUtil.doSm1AesEncrypt(smsDetail.getToPhone()));
                smsDetail.setSupplierOrder(smsBatchAddBo.getSupplierOrder());
                smsDetail.setTemplateCode(templateCode);
                smsDetail.setMessageContent(setMessageContent(smsDetail.getItemJson(), templateContent));
                smsDetail.setTenantId(tenantId);
                smsDetail.setCreateUser(userId);
            }
            String currTime = CommonDateUtil.getCurrentTimeStr();
            List<List<GzxSmsDetail>> bacthList = Lists.partition(smsDetailList, IntConstant.VALUE_1000);
            for (List<GzxSmsDetail> smsDetailBatch : bacthList) {
                String batchSql = smsDetailBatch.stream()
                        .map(s -> String.format("('%s', '%s','%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
                                escapeSql(s.getBatchNumber()),
                                escapeSql(s.getProjectCode()),
                                escapeSql(s.getSignCity()),
                                escapeSql(s.getToPhone()),
                                escapeSql(s.getSupplierOrder()),
                                escapeSql(s.getItemJson()),
                                escapeSql(s.getTemplateCode()),
                                escapeSql(s.getMessageContent()),
                                escapeSql(s.getTenantId()),
                                escapeSql(s.getCreateUser()),
                                escapeSql(currTime)
                        ))
                        .collect(Collectors.joining(", ")).replace("'null'", "null");
                smsDetailMapper.insertBatchSql(batchSql);
                CommonSleepUtil.sleep(IntConstant.VALUE_100);
            }
            GzxSmsBatch smsBatch = new GzxSmsBatch();
            smsBatch.setBatchNumber(sb.toString());
            smsBatch.setBatchName(smsBatchAddBo.getBatchName());
            smsBatch.setProjectCode(projectCode);
            smsBatch.setSupplierOrder(smsBatchAddBo.getSupplierOrder());
            smsBatch.setTemplateCode(templateCode);
            smsBatch.setBatchStatus(GzxSmsBatchStatusEnum.PENDING_TEST.getCode());
            smsBatch.setTotalQuantity(smsDetailList.size());
            smsBatch.setCreateDept(Long.valueOf(StpLoginUserUtil.getLoginUser().getOrgId()));
            smsBatchMapper.insert(smsBatch);
            log.info("导入成功，共{}条，耗时{}秒", smsDetailList.size(), System.currentTimeMillis() - start);
        }
        return str.toString();
    }

    //过滤短信白名单
    private List<GzxSmsDetail> getSmsDetails(String productCode, List<GzxSmsDetail> smsDetailList, StringBuilder str) {
        //获取当月所有白名单
        GzxSmsWhitelistQueryBo queryBo = new GzxSmsWhitelistQueryBo();
        queryBo.setCurrentMonth(DateUtil.format(new Date(), "yyyy-MM"));
        queryBo.setProductCode(productCode);
        List<GzxSmsWhitelist> whitelists = whitelistService.queryByCurrentMonth(queryBo);
        // 白名单为空或不足100条，可能当月没有导入白名单，则发送短信
        if (!whitelists.isEmpty()) {
            List<GzxSmsDetail> originList = smsDetailList;
            //原来有多少条
            int before = originList.size();
            // smsDetailList 的手机号在白名单里的才可以发送，取他俩的交集
            Set<String> whitelistPhones = whitelists.stream().map(GzxSmsWhitelist::getPhone).collect(Collectors.toSet());
            smsDetailList = originList.stream().filter(detail -> whitelistPhones.contains(detail.getToPhone())).toList();
            //过滤掉的手机号
            List<GzxSmsDetail> list = originList.stream().filter(detail -> !whitelistPhones.contains(detail.getToPhone())).toList();
            //过滤之后还有多少条
            int after = smsDetailList.size();
            int num = before - after;
            //过滤掉的手机号给它一个提示0-10条，大于10条的后面加...
            str.append("以下是非本人手机号不允许发送：");
            if (num > 10) {
                for (int i = 0; i < 10; i++) {
                    str.append(list.get(i).getToPhone()).append(",");
                }
                str.append("...共 ").append(num).append(" 条");
            } else if (num > 0 && num < 10) {
                //过滤掉的小于10条
                for (GzxSmsDetail gzxSmsDetail : list) {
                    str.append(gzxSmsDetail.getToPhone()).append(",");
                }
                str.append("共 ").append(num).append(" 条");
            }
        } else {
            str.append("未配置白名单 - 项目编码：").append(productCode);
        }
        return smsDetailList;
    }

    /**
     * 发送测试短信
     *
     * @param batchNumber
     */
    @Override
    public void testSend(String batchNumber) {
        List<GzxSmsDetail> smsDetailList = smsDetailMapper.selectList(Wrappers.<GzxSmsDetail>lambdaQuery()
                .eq(GzxSmsDetail::getBatchNumber, batchNumber)
                .last("limit 1"));
        if (CollectionUtil.isNotEmpty(smsDetailList)) {
            GzxSmsDetail smsDetail = smsDetailList.getFirst();
            smsBatchMapper.update(Wrappers.<GzxSmsBatch>lambdaUpdate()
                    .setSql("total_quantity = total_quantity + 1,batch_status = " + GzxSmsBatchStatusEnum.PENDING_START.getCode())
                    .eq(GzxSmsBatch::getBatchNumber, smsDetail.getBatchNumber()));
            smsDetail.setId(null);
            String currentPhone = StpLoginUserUtil.getLoginUser().getPhone();
            if (StringUtils.isBlank(currentPhone)) {
                throw new CommonException("发送测试短信失败，请先在个人中心补全手机号");
            }
            smsDetail.setToPhone(currentPhone);
            smsDetailMapper.insert(smsDetail);
            freezeAmountAndSendSms(smsDetail.getSupplierOrder().split(",")[0], List.of(smsDetail));
        }
    }

    /**
     * 开始发送短信
     *
     * @param batchNumber
     */
    @Override
    public void startSend(String batchNumber) {
        GzxSmsBatch smsBatch = smsBatchMapper.selectOne(Wrappers.<GzxSmsBatch>lambdaQuery()
                .eq(GzxSmsBatch::getBatchNumber, batchNumber));
        if (!smsBatch.getBatchStatus().equals(GzxSmsBatchStatusEnum.PENDING_START.getCode())) {
            throw new CommonException("当前短信状态非" + GzxSmsBatchStatusEnum.PENDING_START.getMessage());
        }
        smsBatch.setBatchStatus(GzxSmsBatchStatusEnum.SENDING.getCode());
        smsBatchMapper.updateById(smsBatch);
    }

    /**
     * 导出短信发送明细
     *
     * @param smsBatchQueryBo
     */
    @Override
    public void export(GzxSmsBatchQueryBo smsBatchQueryBo, HttpServletResponse httpServletResponse) {
        List<GzxSmsDetailVo> smsDetailVoList = smsDetailMapper.selectVoList(buildDetailQueryWrapper(smsBatchQueryBo));
        transService.transMore(smsDetailVoList);
        CommonExcelUtil.exportExcel("短信明细导出", smsDetailVoList, GzxSmsDetailVo.class, httpServletResponse);
    }

    /**
     * 自动发送短信
     */
    @Override
    public void autoSend() {
        //查询今天的发送中的批次
        List<GzxSmsBatch> smsBatcheList = smsBatchMapper.selectList(Wrappers.<GzxSmsBatch>lambdaQuery()
                .eq(GzxSmsBatch::getBatchStatus, GzxSmsBatchStatusEnum.SENDING.getCode())
                .gt(GzxSmsBatch::getCreateTime, CommonDateUtil.getTodayStr()));
        if (CollectionUtil.isNotEmpty(smsBatcheList)) {
            List<String> bactchNumberList = smsBatcheList.stream().map(GzxSmsBatch::getBatchNumber).toList();
            //查询发送失败的短信
            List<GzxSmsDetail> smsDetailList = smsDetailMapper.selectList(Wrappers.<GzxSmsDetail>lambdaQuery()
                    .in(GzxSmsDetail::getBatchNumber, bactchNumberList)
                    .in(GzxSmsDetail::getSendStatus, GzxSmsSendStatusEnum.PENDING.getCode(), GzxSmsSendStatusEnum.FAIL.getCode())
                    .last("limit 300"));
            //短信明细
            if (CollectionUtil.isNotEmpty(smsDetailList)) {
                //根据供应商分组
                Map<String, List<GzxSmsDetail>> supplierMap = processSupplierOrders(smsDetailList);
                for (Map.Entry<String, List<GzxSmsDetail>> supplier : supplierMap.entrySet()) {
                    //冻结金额
                    freezeAmountAndSendSms(supplier.getKey(), supplier.getValue());
                }
            }
        }
    }

    /**
     * 短信发送统计
     */
    @Override
    public GzxStatisticVo smsStatistic(String batchNumber) {
        GzxStatisticVo statisticVo = new GzxStatisticVo();
        try {
            //获取总条数，供应商顺序(逗号分隔)
            GzxSmsStatisticVo quantityOrder = getQuantityOrder(batchNumber);
            if (Objects.isNull(quantityOrder) || StringUtils.isEmpty(quantityOrder.getSupplierOrder())) {
                log.warn("未获取到供应商顺序和当前批次号发送的总条数 {}", batchNumber);
                return statisticVo;
            }
            //供应商顺序
            String[] supplierOrder = quantityOrder.getSupplierOrder().split(",");
            //当前批次发送的总条数
            int totalQuantity = quantityOrder.getTotalQuantity();
            statisticVo.setTotalQuantity(totalQuantity);
            //供应商代码
            String supplierCode = supplierOrder[0];

            List<GzxSmsStatisticVo> statisticVoList = new ArrayList<>();
            //获取供应商的统计数量
            List<GzxStatisticNumVo> statistic = getStatistic(batchNumber);
            //根据供应商和标志列，转化成 GzxSmsStatisticVo 对象
            for (GzxStatisticNumVo item : statistic) {
                GzxSmsStatisticVo vo = new GzxSmsStatisticVo();
                //供应商Code
                vo.setSupplierCode(item.getSupplierCode());
                if (SMSConstant.SUCCESS.equals(item.getFlag())) {
                    //第一个供应商是成功率，其它的是提升率
                    if (supplierCode.equals(item.getSupplierCode())) {
                        vo.setSuccessNum(item.getCnt());
                        //计算成功率
                        vo.setSuccessRate((double) item.getCnt() / totalQuantity);
                    } else {
                        vo.setSuccessNum(item.getCnt());
                        //计算成功提升率
                        vo.setSuccessImprovementRate((double) item.getCnt() / totalQuantity);
                    }
                } else if (SMSConstant.FAILED.equals(item.getFlag())) {
                    vo.setFailedNum(item.getCnt());
                } else if (SMSConstant.COST.equals(item.getFlag())) {
                    vo.setBillableNum(item.getCnt());
                } else if (SMSConstant.UNKNOW.equals(item.getFlag())) {
                    vo.setUnknowNum(item.getCnt());
                }
                statisticVoList.add(vo);
            }

            //相同的供应商属性合并到一起
            List<GzxSmsStatisticVo> list = mergeStatisticLists(statisticVoList);

            //有成功率的供应商放在第一位
            list = list.stream().sorted(Comparator.comparing(
                    statics -> statics.getSuccessRate() == 0, // 判断 name 是否为空
                    Comparator.nullsLast(Comparator.naturalOrder()) // 将 null 排在后面
            )).toList();
            statisticVo.setStatisticVoList(list);
        } catch (Exception e) {
            log.error("短信发送统计异常 ", e);
        }

        return statisticVo;
    }

    /**
     * 合并两个统计列表
     */
    private List<GzxSmsStatisticVo> mergeStatisticLists(List<GzxSmsStatisticVo> combinedList) {
        // 按 supplierCode 分组并合并
        Map<String, GzxSmsStatisticVo> mergedMap = combinedList.stream()
                .collect(Collectors.toMap(
                        GzxSmsStatisticVo::getSupplierCode, // 以 supplierCode 为 Key
                        Function.identity(), // Value 为对象本身
                        (existing, replacement) -> { // 合并逻辑
                            existing.setSuccessNum(existing.getSuccessNum() + replacement.getSuccessNum());
                            existing.setFailedNum(existing.getFailedNum() + replacement.getFailedNum());
                            existing.setUnknowNum(existing.getUnknowNum() + replacement.getUnknowNum());
                            existing.setBillableNum(existing.getBillableNum() + replacement.getBillableNum());
                            existing.setSuccessRate(existing.getSuccessRate() + replacement.getSuccessRate());
                            existing.setSuccessImprovementRate(existing.getSuccessImprovementRate() + replacement.getSuccessImprovementRate());
                            return existing;
                        }
                ));

        // 将 Map 转换为 List
        return new ArrayList<>(mergedMap.values());
    }

    /**
     * 获取供应商顺序，这批发送了多少条
     */
    @Override
    public GzxSmsStatisticVo getQuantityOrder(String batchNumber) {
        return smsBatchMapper.getQuantityOrder(batchNumber);
    }

    @Override
    public List<GzxSmsStatisticVo> getSuccessFailedNum(String batchNumber) {
        return smsBatchMapper.getSuccessFailedNum(batchNumber);
    }

    @Override
    public List<GzxSmsStatisticVo> getUnknowNum(String batchNumber) {
        return smsBatchMapper.getUnknowNum(batchNumber);
    }

    @Override
    public List<GzxStatisticNumVo> getStatistic(String batchNumber) {
        return smsBatchMapper.getStatistic(batchNumber);
    }

    /**
     * 扫描未开始的短信
     */
    @Override
    public void smsWarn() {
        List<GzxSmsBatchVo> smsBatchList = smsBatchMapper.selectVoList(Wrappers.<GzxSmsBatch>lambdaQuery()
                .select(GzxSmsBatch::getBatchNumber, GzxSmsBatch::getProjectCode, GzxSmsBatch::getTotalQuantity, GzxSmsBatch::getCreateUser)
                .gt(GzxSmsBatch::getCreateTime, CommonDateUtil.getTodayStr()));
        if (CollectionUtil.isNotEmpty(smsBatchList)) {
            transService.transMore(smsBatchList);
            Map<String, GzxSmsBatchVo> batchNumberMap = smsBatchList.stream()
                    .collect(Collectors.toMap(GzxSmsBatchVo::getBatchNumber, Function.identity()));
            List<GzxSmsDetailCount> smsDetailCountList = smsDetailMapper.countByBatchNumber(new ArrayList<>(batchNumberMap.keySet()));
            Map<String, JSONObject> userMap = userCaffeine.getUserMapByTenantId(GzxTenantConstant.GZX_TENANT_ID);
            String content = "：短信发送未完成，请关注发送进度，当前发送进度：";
            for (GzxSmsDetailCount smsDetailCount : smsDetailCountList) {
                if (smsDetailCount.getNoSendQuantity() > 0) {
                    GzxSmsBatchVo smsBatch = batchNumberMap.get(smsDetailCount.getBatchNumber());
                    String userId = smsBatch.getCreateUser();
                    Integer afs = smsDetailCount.getSuccessQuantity() + smsDetailCount.getFailQuantity();
                    BigDecimal result = BigDecimal.ZERO;
                    if (afs > 0 && new BigDecimal(afs).compareTo(result) != 0) {
                        result = NumberUtil.div(afs, smsBatch.getTotalQuantity(), 4);
                    }
                    if (userMap.containsKey(userId)) {
                        JSONObject userJson = userMap.get(userId);
                        String empNo = userJson.getStr("empNo");
                        if (StringUtils.isNotBlank(empNo)) {
                            CommonWechatUtil.sendMessage("短信告警", smsBatch.getProjectName() + "--" + smsDetailCount.getBatchNumber() + content + result + "%", WechatNotificationType.TASK_ERROR, List.of(empNo));
                        } else {
                            CommonWechatUtil.sendMessage("短信告警", smsBatch.getProjectName() + "--" + smsDetailCount.getBatchNumber() + content + result + "%", WechatNotificationType.TASK_ERROR, List.of("ZhuJingTian-BeiJing"));
                        }
                    } else {
                        CommonWechatUtil.sendMessage("短信告警", smsBatch.getProjectName() + "--" + smsDetailCount.getBatchNumber() + content + result + "%", WechatNotificationType.TASK_ERROR, List.of("ZhuJingTian-BeiJing"));
                    }
                }
            }
        }
    }

    private LambdaQueryWrapper<GzxSmsDetail> buildDetailQueryWrapper(GzxSmsBatchQueryBo smsBatchQueryBo) {
        return Wrappers.<GzxSmsDetail>lambdaQuery()
                .eq(GzxSmsDetail::getBatchNumber, smsBatchQueryBo.getBatchNumber())
                .eq(StringUtils.isNotBlank(smsBatchQueryBo.getPhone()), GzxSmsDetail::getToPhone, CommonCryptogramUtil.doSm1AesEncrypt(smsBatchQueryBo.getPhone()))
                .eq(smsBatchQueryBo.getDetailStatus() != null, GzxSmsDetail::getSendStatus, smsBatchQueryBo.getDetailStatus())
                .orderByDesc(GzxSmsDetail::getId);
    }

    private String setMessageContent(String itemJson, String templateContent) {
        Map<String, Object> itemMap = JSON.parseObject(itemJson, Map.class);
        for (Map.Entry<String, Object> entry : itemMap.entrySet()) {
            templateContent = templateContent.replace(entry.getKey(), String.valueOf(entry.getValue()));
        }
        return templateContent;
    }

    /**
     * 根据supplierOrder和supplierCode获取下一个供应商编码
     *
     * @param supplierOrder 供应商顺序字符串，以逗号分隔
     * @param supplierCode  当前供应商编码
     * @return 下一个供应商的编码，如果当前供应商是最后一个或为空，则返回null
     */
    private String getNextSupplierCode(String supplierOrder, String supplierCode) {
        if (StringUtils.isBlank(supplierOrder)) {
            return null;
        }
        String[] suppliers = supplierOrder.split(",");
        if (StringUtils.isBlank(supplierCode)) {
            return suppliers[0];
        }
        for (int i = 0; i < suppliers.length; i++) {
            if (suppliers[i].equals(supplierCode)) {
                return (i + 1 < suppliers.length) ? suppliers[i + 1] : null;
            }
        }
        return null;
    }

    /**
     * 处理多个supplierOrder和supplierCode组合，生成Map
     *
     * @param smsDetailList 包含supplierOrder和supplierCode的集合
     * @return Map，其中Key是supplierCode，Value是下一个供应商编码
     */
    private Map<String, List<GzxSmsDetail>> processSupplierOrders(List<GzxSmsDetail> smsDetailList) {
        Map<String, List<GzxSmsDetail>> result = new HashMap<>();
        for (GzxSmsDetail smsDetail : smsDetailList) {
            String nextSupplierCode = getNextSupplierCode(smsDetail.getSupplierOrder(), smsDetail.getSupplierCode());
            if (nextSupplierCode != null) {
                // 如果Map中没有对应的Key，初始化一个新列表
                result.computeIfAbsent(nextSupplierCode, k -> new ArrayList<>()).add(smsDetail);
            }
        }
        return result;
    }

    private void freezeAmountAndSendSms(String supplierCode, List<GzxSmsDetail> smsDetailList) {
        //先根据租户分组
        Map<String, List<GzxSmsDetail>> tenantMap = smsDetailList.stream().collect(Collectors.groupingBy(GzxSmsDetail::getTenantId));
        for (Map.Entry<String, List<GzxSmsDetail>> entry : tenantMap.entrySet()) {
            //根据项目分组
            Map<String, List<GzxSmsDetail>> projectMap = entry.getValue().stream().collect(Collectors.groupingBy(GzxSmsDetail::getProjectCode));
            for (Map.Entry<String, List<GzxSmsDetail>> entry2 : projectMap.entrySet()) {
                List<List<GzxSmsDetail>> bactList = Lists.partition(entry2.getValue(), IntConstant.VALUE_300);
                for (List<GzxSmsDetail> batch : bactList) {
                    GzxTransMethodBo transMethodBo = new GzxTransMethodBo();
                    transMethodBo.setSupplierCode(supplierCode);
                    transMethodBo.setProjectCode(entry2.getKey());
                    transMethodBo.setRowNum(batch.size());
                    transMethodBo.setTenantId(entry.getKey());
                    transMethodBo.setFoundsType(FoundsConstant.FOUNDS_TYPE_SMS);
                    transMethodBo.setTransType(FoundsConstant.TRANS_TYPE_FREEZE);
                    if (foundsFlowService.transFreeze(transMethodBo)) {
                        SMSStrategy smsStrategy = smsStrategyFactory.getStrategy(supplierCode);
                        smsStrategy.sendProcess(smsDetailList);
                    }
                }
            }
        }
    }
}
